package com.demo.algorithm.HouseRobber07;

import java.util.Arrays;

/**
 * Author:       zhangshaoyang
 * Description:
 * 你是一个专业的小偷，计划偷窃沿街的房屋。每间房内都藏有一定的现金，影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统，如果两间相邻的房屋在同一晚上被小偷闯入，系统会自动报警。
 * <p>
 * 给定一个代表每个房屋存放金额的非负整数数组，计算你 不触动警报装置的情况下 ，一夜之内能够偷窃到的最高金额。
 * <p>
 *  
 * <p>
 * 示例 1：
 * <p>
 * 输入：[1,2,3,1]
 * 输出：4
 * 解释：偷窃 1 号房屋 (金额 = 1) ，然后偷窃 3 号房屋 (金额 = 3)。
 *      偷窃到的最高金额 = 1 + 3 = 4 。
 * 示例 2：
 * <p>
 * 输入：[2,7,9,3,1]
 * 输出：12
 * 解释：偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9)，接着偷窃 5 号房屋 (金额 = 1)。
 *      偷窃到的最高金额 = 2 + 9 + 1 = 12 。
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/house-robber
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 * Date:         2021/7/15 2:35 下午
 */
public class HouseRobber1 {

    private static int[] memo;

    //主函数
    public static int rob(int[] nums) {
        //初始化备忘录
        memo = new int[nums.length];
        Arrays.fill(memo, -1);
        //从第0间房子开始抢劫
        return dp(nums, 0);
    }

    //返回dp[start……]能抢到的最大值
    private static int dp(int[] nums, int start) {
        if (start >= nums.length) {
            return 0;
        }
        //避免重复计算
        if (memo[start] != -1) {
            return memo[start];
        }

        System.out.print("start = " + start);
        System.out.println(" ----> nums[" + start + "] = " + nums[start]);
        int res = Math.max(
                //不抢，去下一家
                dp(nums, start + 1),
                //抢，去下下家
                nums[start] + dp(nums, start + 2));
        System.out.println("res = " + res);
        //计入备忘录
        memo[start] = res;
        return res;
    }

    /**
     * 优化1
     * rob()方法是从前遍历到最后，再从后向前加
     * rob()是自顶向下的动态规划解法，我们也可以略作修改，写出自底向上的解法：rob1()
     *
     * @param nums
     * @return
     */
    public static int rob1(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n + 2];
        for (int start = n - 1; start >= 0; start--) {
//            System.out.print("start = " + start);
//            System.out.println(" ----> nums[" + start + "] = " + nums[start]);
//            System.out.println("dp[" + (start + 1) + "] = " + dp[start + 1]);
//            System.out.println("dp[" + (start + 2) + "] = " + dp[start + 2]);
            dp[start] = Math.max(dp[start + 1], nums[start] + dp[start + 2]);
        }
        return dp[0];
    }

    /**
     * 优化2
     * 我们又发现rob1()状态转移只和 dp[i] 最近的两个状态有关，所以可以进一步优化，将空间复杂度降低到 O(1)。
     *
     * @param nums
     * @return
     */
    public static int rob2(int[] nums) {
        int n = nums.length;
        int dp_i1 = 0, dp_i2 = 0;
        int dp_i = 0;
        for (int start = n - 1; start >= 0; start--) {
//            System.out.print("start = " + start);
//            System.out.println(" ----> nums[" + start + "] = " + nums[start]);
//            System.out.println("dp_i1 = " + dp_i1);
//            System.out.println("dp_i2 = " + dp_i2);
            dp_i = Math.max(dp_i1, nums[start] + dp_i2);
            dp_i2 = dp_i1;
            dp_i1 = dp_i;
        }
        return dp_i;
    }

    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 1};
        int[] nums1 = {2, 7, 9, 3, 1};
//        int rob = rob(nums);
        int rob1 = rob2(nums1);
//        System.out.println(rob);
        System.out.println(rob1);
    }
}
